package gui;

import coffee.CoffeeModel;
import coffee.Constants;
import coffee.ServerMessage;
import coffee.Statistics;
import coffee.User;
import coffee.listeners.NewMessageEvent;
import coffee.listeners.NewMessageListener;
import gui.listeners.GuiActionEvent;
import gui.listeners.GuiActionListener;
import gui.model.DrinkModel;
import gui.panels.FinishPanel;
import gui.panels.GuiPanel;
import gui.panels.NavigationPanel;
import gui.panels.SelectCoffeeAdditionsPanel;
import gui.panels.SelectBeansPanel;
import gui.panels.SelectBrewTimePanel;
import gui.panels.SelectCoffeePanel;
import gui.panels.SelectDrinkPanel;
import gui.panels.SelectFavoritePanel;
import gui.panels.SelectSizePanel;
import gui.panels.SelectTeaPanel;
import gui.panels.StatisticsPanel;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.io.IOException;
import java.net.ConnectException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import sun.awt.WindowClosingListener;

/**
 *
 * @author Benni
 */
public class MainFrame extends JFrame implements GuiActionListener, NewMessageListener {
    private File checkFile = new File("instance.inst");
    
    private JPanel contentPanel;
    private NavigationPanel navigationPanel;
    public SelectDrinkPanel selectDrinkPanel;
    public SelectSizePanel selectSizePanel;
    public SelectSizePanel selectEspressoSizePanel;
    public SelectCoffeePanel selectCoffeePanel;
    public SelectBeansPanel selectBeansPanel;
    public SelectCoffeeAdditionsPanel selectCoffeeAdditionsPanel;
    public SelectCoffeeAdditionsPanel selectCappuchinoAdditionsPanel;
    public SelectTeaPanel selectTeaPanel;
    public SelectBrewTimePanel selectBrewTimePanel;
    public FinishPanel finishPanel;
    public StatisticsPanel statisticsPanel;
    public SelectFavoritePanel selectFavoritePanel;
    
    private BlockingDialog blockingDialog;
    private boolean stopBlocking = false;
    
    private List<GuiPanel> contentPanels = new ArrayList<>();
    public GuiPanel previousPanel;
    
    public static CoffeeModel model;
    private CardLayout layout;
    private GuiPanel currentPanel;
    
    private Process server;
    private ServerPrinter printer;
    
    private boolean waitingForAnswer = false, addStatistic = false;
    private String answer;
    
    public static int USER_ID = 0;
    public static User user;
    
    public Dimension size = new Dimension(1050, 720);
    
    private void doLogin() {
        List<User> users = User.getUsers();
        if(users.isEmpty()) {
            addNewUser();
        } else {
            String[] names = new String[users.size() + 1];
            for(int i = 0; i< users.size(); i++) {
                names[i] = users.get(i).getName();
            }
            String newUserString = "Neues Konto anlegen...";
            names[users.size()] = newUserString;
            
            String value = (String)JOptionPane.showInputDialog(this, "Bitte loggen Sie sich ein", "Login", JOptionPane.PLAIN_MESSAGE, null, names, users.get(0).getName());
            if(value == null) {
                checkFile.delete();
                System.exit(1);
            }
                
            
            if(value.equals(newUserString)) {
                addNewUser();
            } else {
                for(User u : users) {
                    if(u.getName().equals(value)) {
                        user = u;
                        return;
                    }
                }
                throw new RuntimeException("Benutzer ausgewählt, der nicht existiert!");
            }
        }
    }
    
    private void addNewUser() {
        String value;
            // Name eingeben
            do{
                value = (String)JOptionPane.showInputDialog(this, "Bitte geben Sie Ihren Namen ein:","Benutzer anlegen", JOptionPane.INFORMATION_MESSAGE);
                if(value == null) {
                    checkFile.delete();
                    System.exit(1);
                }
                    
            } while(value.isEmpty());
            String name = value;
            
            // Gewicht eingeben
            int weight = 0;
            do{
                try {
                    value = (String)JOptionPane.showInputDialog(this, "Bitte geben Sie Ihr Gewicht ein:","Benutzer anlegen", JOptionPane.INFORMATION_MESSAGE);
                    if(value == null) {
                        checkFile.delete();
                        System.exit(1);
                    }
                    weight = Integer.parseInt(value);
                    if(weight < 0)
                        value = null;
                }
                catch(NumberFormatException e) {
                    value = null;
                }
            } while(value == null || value.isEmpty());
            
            // Büronummer eingeben
            do{
                value = (String)JOptionPane.showInputDialog(this, "Bitte geben Sie Ihre Büronummer ein:","Benutzer anlegen", JOptionPane.INFORMATION_MESSAGE);
                if(value == null) {
                    checkFile.delete();
                    System.exit(1);
                }
            } while(value.isEmpty());
            String office = value;
            
            user = new User(name, weight, office);
    }
    
    private void checkInstances() {
        if(checkFile.exists()) {
            JOptionPane.showMessageDialog(this, "Es scheint noch eine Instanz des Programms geöffnet zu sein.\nSollte dies nicht der Fall sein,\nlöschen Sie bitte die Datei \"instance.inst\" und\nversuchen Sie erneut das Programm zu starten.", "Hinweis", JOptionPane.INFORMATION_MESSAGE);
            System.exit(1);
        } else {
            try {
                checkFile.createNewFile();
            }
            catch(IOException e) {
                e.printStackTrace();
                System.exit(1);
            }
        }
    }
    
    public MainFrame() {
        super("Moccael Angelo");
        checkInstances();
        doLogin();
        Constants.initMap();
        
        try {
            String path = "." + File.separatorChar + "server" + File.separatorChar + "MMIBeverages.jar";
            server = Runtime.getRuntime().exec("java -jar " + path);
            printer = new ServerPrinter(server);
            new Thread(printer).start();
        } catch (IOException ex) {
            Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        // Workaround... Verzögerung, da sonst Socketverbindung bevor Server gestartet
        try{
            Thread.sleep(500);
        } catch(InterruptedException e){}
        try {
            model = new CoffeeModel();
            model.addNewMessageListener(this);
        }
        catch(Exception e) {
            JOptionPane.showMessageDialog(this, "Verbindung zum Server konnte nicht hergestellt werden!", "Fehler", JOptionPane.ERROR_MESSAGE);
            System.out.println("");
            System.exit(1);
        } 
        
        setSize(size);
        setResizable(false);
//        setUndecorated(true);
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        addWindowListener( new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent we) {
                quit();
            }
        } );
        setLocationRelativeTo(null);
      
        initComponents();
        setVisible(true);
        
    }
    
    private void initComponents() {
        setLayout(new GridBagLayout());
        
        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;
        c.gridx = 0;
        c.gridy = GridBagConstraints.RELATIVE;
        c.weightx = 1;
        c.weighty = 1;
        
        contentPanel = new JPanel();
        layout = new CardLayout();
        contentPanel.setLayout(layout);
        add(contentPanel, c);
        
        selectDrinkPanel = new SelectDrinkPanel(this);
        contentPanels.add(selectDrinkPanel);
        
        selectSizePanel = new SelectSizePanel(this);
        contentPanels.add(selectSizePanel);
        
        selectEspressoSizePanel = new SelectSizePanel(this, true);
        contentPanels.add(selectEspressoSizePanel);
        
        selectCoffeePanel = new SelectCoffeePanel(this);
        contentPanels.add(selectCoffeePanel);
        
        selectBeansPanel = new SelectBeansPanel(this);
        contentPanels.add(selectBeansPanel);
        
        selectCoffeeAdditionsPanel = new SelectCoffeeAdditionsPanel(this, false);
        contentPanels.add(selectCoffeeAdditionsPanel);
        
//        selectCappuchinoAdditionsPanel = new SelectCappuchinoAdditionsPanel(this);
        selectCappuchinoAdditionsPanel = new SelectCoffeeAdditionsPanel(this, true);
        contentPanels.add(selectCappuchinoAdditionsPanel);
        
        selectTeaPanel = new SelectTeaPanel(this);
        contentPanels.add(selectTeaPanel);
        
        selectBrewTimePanel = new SelectBrewTimePanel(this);
        contentPanels.add(selectBrewTimePanel);
        
        finishPanel = new FinishPanel(this);
        contentPanels.add(finishPanel);
        
        statisticsPanel = new StatisticsPanel(this);
        contentPanels.add(statisticsPanel);
        
        selectFavoritePanel = new SelectFavoritePanel(this);
        contentPanels.add(selectFavoritePanel);
        
        for(GuiPanel g : contentPanels) {
            g.addGuiActionEventListener(this);
            contentPanel.add(g, g.getName());
        }
        
        navigationPanel = new NavigationPanel(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if(e.getSource() == navigationPanel.nextButtonCoffee || e.getSource() == navigationPanel.nextButtonTea) {
                    navigateForward();
                } else {
                    navigateBack();
                }
            }
        });
        c.weighty = 0;
        add(navigationPanel, c);
        
        switchContentPanel(selectDrinkPanel.getName());
    }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new MainFrame();
            }
        });
    }
    
    private void navigateBack() {
        GuiPanel panel = currentPanel.getPreviousPanel();
        if(panel != null)
            switchContentPanel(panel.getName());
    }
    
    private void navigateForward() {
        GuiPanel panel = currentPanel.getNextPanel();
        if(panel != null)
            switchContentPanel(panel.getName());
    }

    @Override
    public void GuiActionEventFired(GuiActionEvent event) {
        switch(event.getType()) {
            case CHOSE_COFFEE:
                DrinkModel.getInstance().setDrink(DrinkModel.Drink.COFFEE);
                switchContentPanel(selectCoffeePanel.getName());
                break;
            case CHOSE_TEA:
                DrinkModel.getInstance().setDrink(DrinkModel.Drink.TEA);
                switchContentPanel(selectTeaPanel.getName());
                break;
            case CHOSE_COFFEE_BLACK:
                DrinkModel.getInstance().setCoffee(DrinkModel.Coffee.BLACK_COFFEE);
                switchContentPanel(selectSizePanel.getName());
                break;
            case CHOSE_CAPPUCHINO:
                DrinkModel.getInstance().setCoffee(DrinkModel.Coffee.CAPPUCHINO);
                switchContentPanel(selectSizePanel.getName());
                break;
            case CHOSE_ESPRESSO:
                DrinkModel.getInstance().setCoffee(DrinkModel.Coffee.ESPRESSO);
                switchContentPanel(selectEspressoSizePanel.getName());
                break;
            case CHOSE_BIG:
                DrinkModel.getInstance().setSize(DrinkModel.Size.BIG);
                sizeChosen();
                break;
            case CHOSE_MEDIUM:
                DrinkModel.getInstance().setSize(DrinkModel.Size.MEDIUM);
                sizeChosen();
                break;
            case CHOSE_SMALL:
                DrinkModel.getInstance().setSize(DrinkModel.Size.SMALL);
                sizeChosen();
                break;
            case CHOSE_ARABICA:
                DrinkModel.getInstance().setBean(DrinkModel.Bean.ARABICA);
                beansChosen();
                break;
            case CHOSE_ROBUSTA:
                DrinkModel.getInstance().setBean(DrinkModel.Bean.ROBUSTA);
                beansChosen();
                break;
            case CHOSE_GREEN_TEA:
                DrinkModel.getInstance().setTea(DrinkModel.Tea.GREEN_TEA);
                switchContentPanel(selectSizePanel.getName());
                break;
            case CHOSE_BLACK_TEA:
                DrinkModel.getInstance().setTea(DrinkModel.Tea.BLACK_TEA);
                switchContentPanel(selectSizePanel.getName());
                break;
            case CHOSE_FRUIT_TEA:
                DrinkModel.getInstance().setTea(DrinkModel.Tea.FRUIT_TEA);
                switchContentPanel(selectSizePanel.getName());
                break;
                
            case TEA_DONE:
                switchContentPanel(finishPanel.getName());
                break;
            case CAPPUCHINO_DONE:
                DrinkModel.getInstance().setCaramel((boolean)event.getData().getData()[0]);
                DrinkModel.getInstance().setHazelnut((boolean)event.getData().getData()[1]);
                switchContentPanel(finishPanel.getName());
                break;
            case COFFEE_DONE:
                DrinkModel.getInstance().setCaramel((boolean)event.getData().getData()[0]);
                DrinkModel.getInstance().setHazelnut((boolean)event.getData().getData()[1]);
                DrinkModel.getInstance().setMilk((boolean)event.getData().getData()[2]);
                switchContentPanel(finishPanel.getName());
                break;
            case DONE:
                makeDrink();
                break;
            case AS_DEFAULT:
                saveAsDefault();
                break;
            case OPEN_STATISTICS:
                previousPanel = currentPanel;
                switchContentPanel(statisticsPanel.getName());
                break;
                
            case SHOW_FAVORITES:
                switchContentPanel(selectFavoritePanel.getName());
                break;
            case MAKE_FAVORITE_TEA:
                makeDrink(user.getFavouriteTea());
                break;
            case MAKE_FAVORITE_COFFEE:
                makeDrink(user.getFavouriteCoffee());
                break;
            case ABORT:
                reset();
                break;
            default:
                throw new AssertionError(event.getType().name());
        }
    }
    
    private void saveAsDefault() {
        switch(DrinkModel.getInstance().getDrink()) {
            case COFFEE:
                user.setFavouriteCoffee(DrinkModel.getInstance().getDrinkRepresentation());
                user.saveUser();
                break;
            case TEA:
                user.setFavouriteTea(DrinkModel.getInstance().getDrinkRepresentation());
                user.saveUser();
                break;
            default:
                throw new AssertionError(DrinkModel.getInstance().getDrink().name());
        }
    }
    
    private void makeDrink(DrinkModel.DrinkRepresantation rep) {
        DrinkModel.getInstance().setToRepresntation(rep);
        makeDrink();
    }
    
    private void makeDrink() {
        DrinkModel m = DrinkModel.getInstance();
        if(m.getDrink() == DrinkModel.Drink.TEA) {
            switch(m.getTea()) {
                case GREEN_TEA:
                    model.makeGreenTea(enumToInt(m.getSize()), m.getTemperature(), m.getBrewTime());
                    break;
                case BLACK_TEA:
                    model.makeBlackTea(enumToInt(m.getSize()), m.getTemperature(), m.getBrewTime());
                    break;
                case FRUIT_TEA:
                    model.makeFruitTea(enumToInt(m.getSize()), m.getTemperature(), m.getBrewTime());
                    break;
            }
        } else if(m.getDrink() == DrinkModel.Drink.COFFEE) {
            switch(m.getCoffee()) {
                case BLACK_COFFEE:
                    model.makeCoffee(enumToInt(m.getBean()), enumToInt(m.getSize()), m.isMilk(), m.isHazelnut(), m.isCaramel());
                    break;
                case CAPPUCHINO:
                    model.makeCappuccino(enumToInt(m.getBean()), enumToInt(m.getSize()), m.isHazelnut(), m.isCaramel());
                    break;
                case ESPRESSO:
                    model.makeEspresso(enumToInt(m.getSize()), m.isHazelnut(), m.isCaramel(), m.isMilk());
                    break;
            }
        }
        // Auf Antwort warten
        waitingForAnswer = true;
        while(waitingForAnswer) {
            try{
                Thread.sleep(50);
            }catch(InterruptedException e){
                
            }
        }
        
        if(checkAnswer(answer).status != ServerMessage.Status.ERROR) {
            if(stopBlocking) {
                stopBlocking = false;
                return;
            }
            blockingDialog = new BlockingDialog(this);
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        blockingDialog.setVisible(true);
                    }
                });
        }
    }
    
    private void addStatistic(ServerMessage message) {
        DrinkModel m = DrinkModel.getInstance();
        if(message.status == ServerMessage.Status.CONFIRM) {
            long timestamp = new Timestamp(System.currentTimeMillis()).getTime();
            
            int drink;
            if(m.getDrink() == DrinkModel.Drink.COFFEE) {
                drink = enumToInt(m.getCoffee());
            } else {
                drink = enumToInt(m.getTea());
            }
            
            int price = message.getPrice();
            int caffein = message.getCaffeine();
            
            user.getStatistic().add(new Statistics(timestamp, drink, price, caffein));
            user.saveUser();
            
            reset();
        }
    }
    
    private ServerMessage checkAnswer(String answer) {
        ServerMessage sm = new ServerMessage(answer);
        
        switch(sm.status) {
            case ERROR:
                JOptionPane.showMessageDialog(this, sm.getMessage(), "Warnung", JOptionPane.ERROR_MESSAGE);
                doJobs(sm);
                break;
                
            case CONFIRM:
                JOptionPane.showMessageDialog(this, sm.getMessage(), "Bestätigung", JOptionPane.INFORMATION_MESSAGE);
                break;
        }
        return sm;
    }
    
    private void doJobs(ServerMessage sm) {
        for(int i : sm.value) {
            switch(i) {
                case Constants.NEEDS_CLEANING:
                    model.cleanMachine();
                    break;
                    
                case Constants.ARABICA:   
                case Constants.ROBUSTA:
                case Constants.ESPRESSO:
                    model.fillCoffee();
                    break;
                    
                case Constants.HAZELNUT:
                case Constants.CARAMEL:
                case Constants.MILK:
                    model.fillExtras();
                    break;
                    
                case Constants.GARBAGE_LEVEL:
                case Constants.GARBAGE_INSUFFICIANT:
                    model.emptyGarbage();
                    break;
                    
                case Constants.GREEN_TEA:    
                case Constants.BLACK_TEA:
                case Constants.FRUIT_TEA:
                    model.fillTea();
                    System.out.println("fdggfghgfhgfhgfh");
                    break;
                    
                    
            }
        }
    }
    
    private void reset() {
        DrinkModel.getInstance().reset();
        switchContentPanel(selectDrinkPanel.getName());
    }
    
    public static int enumToInt(Object o) {
        if(o instanceof DrinkModel.Tea) {
            DrinkModel.Tea t = (DrinkModel.Tea)o;
            switch(t) {
                case GREEN_TEA:
                    return Constants.GREEN_TEA;
                case BLACK_TEA:
                    return Constants.BLACK_TEA;
                case FRUIT_TEA:
                    return Constants.FRUIT_TEA;
            }
        }
        if(o instanceof DrinkModel.Coffee) {
            DrinkModel.Coffee c = (DrinkModel.Coffee)o;
            switch(c) {
                case BLACK_COFFEE:
                    return Constants.COFFEE;
                case CAPPUCHINO:
                    return Constants.CAPPUCCINO;
                case ESPRESSO:
                    return Constants.ESPRESSO;
            }
        }
        if(o instanceof DrinkModel.Size) {
            DrinkModel.Size s = (DrinkModel.Size)o;
            switch(s) {
                case SMALL:
                    return Constants.SMALL_CUP;
                case MEDIUM:
                    return Constants.MEDIUM_CUP;
                case BIG:
                    return Constants.BIG_CUP;
            }
        }
        if(o instanceof DrinkModel.Bean) {
            DrinkModel.Bean b = (DrinkModel.Bean)o;
            switch(b) {
                case ARABICA:
                    return Constants.ARABICA;
                case ROBUSTA:
                    return Constants.ROBUSTA;
            }
        }
        throw new RuntimeException();
    }
    
    private void beansChosen() {
        if(DrinkModel.getInstance().getCoffee() == DrinkModel.Coffee.CAPPUCHINO) {
            switchContentPanel(selectCappuchinoAdditionsPanel.getName());
        } else {
            switchContentPanel(selectCoffeeAdditionsPanel.getName());
        }
    }
    
    private void sizeChosen() {
        if(DrinkModel.getInstance().getDrink() == DrinkModel.Drink.TEA) {
            switchContentPanel(selectBrewTimePanel.getName());
        } else if(DrinkModel.getInstance().getCoffee() == DrinkModel.Coffee.ESPRESSO) {
            switchContentPanel(selectCoffeeAdditionsPanel.getName());
        } else {
            switchContentPanel(selectBeansPanel.getName());
        }
    }

    @Override
    public void processNewMessage(NewMessageEvent event) {
        ServerMessage sm = event.getMessage();
        
        // ServerMessage Testausgabe
        
        System.out.println("Neue Gui-Msg:");
        System.out.println(sm.status + " " + sm.getMessage());
        
        String[] splitted = sm.getOrigMessage().split(" ");
        StringBuilder output = new StringBuilder("Received: ");
        List<Integer> list = new ArrayList<>();
        
        for(int i = 0; i < splitted.length; i++) {
            String s = splitted[i];
            try {
                int number = Integer.parseInt(s);
                list.add(number);
                String map = Constants.map.get(number);
                if(map != null)
                    s = map;
            } catch(NumberFormatException e) {}
            output.append(s);
            
            if(i != splitted.length - 1)
                output.append(" ");
        }
        
        if(addStatistic) {
            addStatistic = false;
            addStatistic(event.getMessage());
            if(blockingDialog == null) {
                stopBlocking = true;
            } else {
                blockingDialog.dispose();
                blockingDialog = null;
            }
        }
        
        if(waitingForAnswer) {
            answer = sm.getOrigMessage();
            waitingForAnswer = false;
            if(sm.status != ServerMessage.Status.ERROR) {
                addStatistic = true;
            }
        }
        
        System.out.println(output.toString());
    }
    
    private void switchContentPanel(String name) {
        layout.show(contentPanel, name);
        
        for(GuiPanel g : contentPanels) {
            if(g.getName().equals(name)) {
                currentPanel = g;
                break;
            }
        }
        navigationPanel.setPreviousButtonEnabled(currentPanel.getPreviousButtonEnabled());
        navigationPanel.setNextButtonEnabled(currentPanel.getNextButtonEnabled());
        navigationPanel.setDescription(currentPanel.getDescription());
        navigationPanel.updateButtons();
        currentPanel.updateFields();
    }
    
    private void quit() {
        model.quit();
        try {
            Thread.sleep(500);
        } catch (InterruptedException ex) {}
        server.destroy();
        printer.stopRunning();
        if(checkFile.exists()) {
            checkFile.delete();
        }
        System.exit(0);
    }
}